#include "tableschema.h"
#include "str.h"
#include <proc/command.h>
#include <proc/reader.h>
#include <proc/exception.h>

TableSchema::TableSchema(Connection &connection, const char *tablename):connection(connection)
{
	this->tablename = tablename;
	Str::upper(this->tablename);
	initTypeMap();
	initTypeMap2();
	unknown = "UNKNOWN";
}

void TableSchema::initTypeMap()
{
	typeMap["UNKNOWN"]   = DBCOL_TYPE_UNKNOW;
	
	typeMap["CHAR"]      = DBCOL_TYPE_CHAR;
	typeMap["CHARACTER"] = DBCOL_TYPE_CHAR;
	typeMap["NCHAR"]     = DBCOL_TYPE_CHAR;

	typeMap["VARCHAR"]   = DBCOL_TYPE_VARCHAR;
	typeMap["NVARCHAR"]  = DBCOL_TYPE_VARCHAR;

	typeMap["VARCHAR2"]  = DBCOL_TYPE_VARCHAR2;
	typeMap["NVARCHAR2"] = DBCOL_TYPE_VARCHAR2;

	typeMap["SHORT"]    = DBCOL_TYPE_SHORT;
	typeMap["NUMBER"]    = DBCOL_TYPE_NUMBER;
	typeMap["INTEGER"]   = DBCOL_TYPE_INTEGER;
	typeMap["FLOAT"]     = DBCOL_TYPE_FLOAT;
	typeMap["DECIMAL"]   = DBCOL_TYPE_DECIMAL;
	typeMap["DATE"]      = DBCOL_TYPE_DATE;
	typeMap["TIMESTAMP"] = DBCOL_TYPE_TIMESTAMP;
	typeMap["INTERVAL"]  = DBCOL_TYPE_INTERVAL;
	typeMap["BLOB"]      = DBCOL_TYPE_BLOB;
	typeMap["MONEY"]     = DBCOL_TYPE_MONEY;
}

void TableSchema::initTypeMap2()
{
	typeMap2[DBCOL_TYPE_UNKNOW   ] = "UNKNOWN";
	typeMap2[DBCOL_TYPE_CHAR     ] = "CHAR";   
	typeMap2[DBCOL_TYPE_VARCHAR  ] = "VARCHAR";
	typeMap2[DBCOL_TYPE_VARCHAR2 ] = "VARCHAR2";
	typeMap2[DBCOL_TYPE_SHORT    ] = "SHORT";  
	typeMap2[DBCOL_TYPE_INTEGER  ] = "INTEGER";
	typeMap2[DBCOL_TYPE_NUMBER   ] = "NUMBER";
	typeMap2[DBCOL_TYPE_FLOAT    ] = "FLOAT";
	typeMap2[DBCOL_TYPE_DECIMAL  ] = "DECIMAL";
	typeMap2[DBCOL_TYPE_MONEY    ] = "MONEY";
	typeMap2[DBCOL_TYPE_DATE     ] = "DATE";
	typeMap2[DBCOL_TYPE_TIMESTAMP] = "TIMESTAMP";
	typeMap2[DBCOL_TYPE_INTERVAL ] = "INTERVAL";
	typeMap2[DBCOL_TYPE_BLOB     ] = "BLOB";
}

bool TableSchema::open()
{
	if (getTableSpace() == false){
		return false;
	}
	if (getColumns() == false){
		return false;
	}
	if (getIndexs() == false){
		return false;
	}
	return true;
}

bool TableSchema::getTableSpace()
{	
	Command command(connection);
	Reader *reader = 0;
	
	table.isView = false;
	command.commandText  = "select tablespace_name,num_rows from user_tables where table_name='";
	command.commandText += tablename;
	command.commandText += "'";
	
	try{
		reader = command.executeReader();
		if (reader->read() == false){
			reader->close();
			return getViewTableSpace();
		}
		table.tbs_name = reader->getData(0);
		table.num_rows = atoi(reader->getData(1));
		reader->close();
	}catch(Exception *e){
		printf("%s\n", command.commandText.c_str());
		printf("oracode=%d\n", e->code());
		printf("%s\n", e->what().c_str());
		if (reader != 0){
			reader->close();
		}
		delete e;
		return false;
	}
	
	Str::trim(table.tbs_name);
	return true;
}

bool TableSchema::getViewTableSpace()
{
	Command command(connection);
	Reader *reader = 0;
	
	command.commandText  = "select tablespace_name,num_rows from user_views where view_name='";
	command.commandText += tablename;
	command.commandText += "'";
	
	try{
		reader = command.executeReader();
		if (reader->read() == false){
			reader->close();
			printf("can not find table:%s\n", tablename.c_str());
			return false;
		}
		table.tbs_name = reader->getData(0);
		table.num_rows = atoi(reader->getData(1));
		table.isView = true;
		reader->close();
	}catch(Exception *e){
		printf("%s\n", command.commandText.c_str());
		printf("oracode=%d\n", e->code());
		printf("%s\n", e->what().c_str());
		if (reader != 0){
			reader->close();
		}
		delete e;
		return false;
	}
	
	Str::trim(table.tbs_name);
	return true;
}

bool TableSchema::getColumns()
{
	Command command(connection);
	Reader *reader = 0;
	
	command.commandText  = "select column_name, data_type, data_length, column_id,";
	command.commandText += "data_precision, data_scale,nullable from user_tab_columns ";
	command.commandText += "where table_name='";
	command.commandText += tablename;
	command.commandText += "' order by column_id";
	
	try{
		reader = command.executeReader();
		while (reader->read()){
			Column col;
			
			string type, nullable;
			type          = reader->getData(1);
			nullable      = reader->getData(6);
			Str::trim(type); 
			Str::trim(nullable);
			
			col.name      = reader->getData(0);
			col.type      = typeMap[type.substr(0,type.length()>9?9:type.length())];
			col.length    = atoi(reader->getData(2));
			col.id        = atoi(reader->getData(3));
			col.precision = atoi(reader->getData(4));
			col.scale     = atoi(reader->getData(5));
			col.nullable  = ((nullable=="Y")?true:false);
						
			table.cols.push_back(col);
		}
		reader->close();
	}catch(Exception *e){
		printf("%s\n", command.commandText.c_str());
		printf("oracode=%d\n", e->code());
		printf("%s\n", e->what().c_str());
		if (reader != 0){
			reader->close();
		}
		delete e;
		return false;
	}
	if (table.cols.size() == 0){
		printf("can not find any columns in table %s\n", tablename.c_str());
		return false;
	}
	
	return true;
}

bool TableSchema::getIndexs()
{
	Command command(connection);
	Reader *reader = 0;
	
	command.commandText  = "select user_indexes.index_name, user_indexes.uniqueness,";
	command.commandText += "user_ind_columns.column_name, user_ind_columns.descend ";
	command.commandText += "from user_indexes,user_ind_columns ";
	command.commandText += "where user_indexes.table_name = '";
	command.commandText += tablename;
	command.commandText += "' and user_ind_columns.index_name = user_indexes.index_name ";
	command.commandText += "order by user_indexes.index_name,user_ind_columns.column_position";
	
	try{
		reader = command.executeReader();
		while (reader->read()){
			string indexName, uniqueness, columnName, descend;
			indexName  = reader->getData(0);
			uniqueness = reader->getData(1);
			columnName = reader->getData(2);
			descend    = reader->getData(3);
			Str::trim(indexName);
			Str::trim(uniqueness);
			Str::trim(columnName);
			Str::trim(descend);
			
			if (table.indexs.size() == 0){
				Index index;
				index.name = indexName;
				index.type = (uniqueness=="UNIQUE"?UniqueIndex:DuplexIndex);
				table.indexs.push_back(index);
			}
			if (table.indexs.back().name != indexName){
				Index index;
				index.name = indexName;
				index.type = (uniqueness=="UNIQUE"?UniqueIndex:DuplexIndex);
				table.indexs.push_back(index);
			}
			IndexColumn indexColumn;
			indexColumn.name = columnName;
			indexColumn.type = (descend=="ASC"?Ascend:Desend);
			table.indexs.back().indexColumns.push_back(indexColumn);
		}
		reader->close();
	}catch(Exception *e){
		printf("%s\n", command.commandText.c_str());
		printf("oracode=%d\n", e->code());
		printf("%s\n", e->what().c_str());
		if (reader != 0){
			reader->close();
		}
		delete e;
		return false;
	}
	
	return true;
}
